package recipese.controller;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.support.SessionStatus;

import recipese.bean.Ingredient;
import recipese.bean.Recipe;
import recipese.bean.ShareRecipe;
import recipese.service.EmailService;
import recipese.service.RecipeService;
import recipese.service.RemoteRecipeService;

import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;



/**
 * This class handles all requests to /mvc/recipes
 * 
 * It provides basic create, update, retrieve and delete methods for Recipe objects
 * 
 * @author adam
 *
 */
@Controller
@RequestMapping("/recipes")
public class RecipeController {

	private static final String LIST_VIEW_NAME = "recipe-list";
	private static final String LIST_REDIRECT_VIEW_NAME = "redirect:recipes/list";
	private static final String FULL_SCREEN_VIEW_NAME = "recipe-full-screen";
	private static final String ADD_FORM_VIEW_NAME = "recipe";
	private static final String ADD_FORM_REDIRECT_VIEW_NAME = "redirect:recipes";
	
	@Autowired
	private EmailService emailService;
	
	@Autowired
	private RecipeService recipeService;
	
	@Autowired
	private RemoteRecipeService remoteRecipeService;
	
	@ModelAttribute("recipes")
	public List<Recipe> populateRecipes() {
		final Map<String, Boolean> sort = new LinkedHashMap<String, Boolean>();
		sort.put("name", Boolean.TRUE);
		
		final UserService userService = UserServiceFactory.getUserService();
		
		if (userService.isUserLoggedIn()) {
			User user = userService.getCurrentUser();
			List<Recipe> recipes = recipeService.getAllRecipes(sort, user.getEmail());
			return recipes;
		} else {
			return new ArrayList<Recipe>();
		}
	}
	
	@ModelAttribute("sharedRecipes")
	public List<ShareRecipe> populateShareRecipes() {
		final Map<String, Boolean> sort = new LinkedHashMap<String, Boolean>();
		sort.put("name", Boolean.TRUE);
		
		final UserService userService = UserServiceFactory.getUserService();
		
		if (userService.isUserLoggedIn()) {
			User user = userService.getCurrentUser();
			List<ShareRecipe> shares = recipeService.getAllShareRecipes(sort, user.getEmail().toLowerCase());
			recipeService.loadRecipes(shares);
			return shares;
		} else {
			return new ArrayList<ShareRecipe>();
		}
	}
	
	@ModelAttribute("recipe")
	public Recipe setUpForm() {
		return new Recipe();
	}
	
	public void loadExistingRecipe(String recipeId, Recipe recipe) {
		Recipe existingRecipe = recipeService.getRecipeById(recipeId);

		recipe.setUserId(existingRecipe.getUserId());
		recipe.setId(existingRecipe.getId());
		recipe.setName(existingRecipe.getName());
		recipe.setInstructions(existingRecipe.getInstructions());
		recipe.getIngredients().clear();
		recipe.getShares().clear();
		recipe.setUrl(existingRecipe.getUrl());
		
		recipeService.loadIngredients(recipe);
		recipeService.loadShares(recipe);
	}
	
	// GET handlers
	@RequestMapping(method = RequestMethod.GET)
	public String view() {
		return ADD_FORM_VIEW_NAME;
	}
	
	@RequestMapping(params = "id", method = RequestMethod.GET)
	public String viewFullScreen(@RequestParam("id") String recipeId, Recipe recipe, BindingResult result, SessionStatus status) {
		loadExistingRecipe(recipeId, recipe);
		
		return FULL_SCREEN_VIEW_NAME;
	}
	
	//TODO this is at path /list should be separated from other methods
	@RequestMapping(value = "/list", method = RequestMethod.GET)
	public String viewList() {
		return LIST_VIEW_NAME;
	}
	
	// POST handlers
	@RequestMapping(params = "save", method = RequestMethod.POST)
	public String save(Recipe recipe, BindingResult result, SessionStatus status) {
		final UserService userService = UserServiceFactory.getUserService();
		
		// TODO add a full validator
		if (!StringUtils.hasText(recipe.getName())) {
			result.rejectValue("name", "field.required", "a recipe name is required");
		}
		
		if (userService.isUserLoggedIn()) {
			//TODO prevent client from changing recipe.userId
			if (!StringUtils.hasText(recipe.getUserId())) {
				User user = userService.getCurrentUser();
				recipe.setUserId(user.getEmail());
			}
		} else {
			result.reject("You must be logged in!");
		}
		
		if (result.hasErrors()) {
			return ADD_FORM_VIEW_NAME;
		} else {
			final String view;
			
			if (!StringUtils.hasText(recipe.getId())) {
				// we are creating a new recipe
				view = ADD_FORM_REDIRECT_VIEW_NAME;
				
				// remove empty strings
				recipe.setId(null);
			} else {
				// we are updating an existing recipe
				view = LIST_REDIRECT_VIEW_NAME;
			}
			
			recipeService.saveRecipe(recipe);
			recipeService.deleteIngredientsByRecipeId(recipe.getId());
			
			for (Ingredient ingredient : recipe.getIngredients()) {
				if (StringUtils.hasText(ingredient.getText())) {
					ingredient.setId(null);
					ingredient.setRecipeId(recipe.getId());
					
					recipeService.saveIngredient(ingredient);
				}
			}
			
			Recipe oldRecipeShares = new Recipe();
			oldRecipeShares.setId(recipe.getId());
			
			recipeService.loadShares(oldRecipeShares);
			
			recipeService.deleteSharesByRecipeId(recipe.getId());
			
			for (ShareRecipe share : recipe.getShares()) {
				if (StringUtils.hasText(share.getUserId())) {
					share.setId(null);
					share.setRecipeId(recipe.getId());
					share.setUserId(share.getUserId().toLowerCase());
					
					recipeService.saveShare(share);
					
					boolean alreadyExists = false;
					
					for (ShareRecipe oldShare : oldRecipeShares.getShares()) {
						if (share.getUserId().equals(oldShare.getUserId())) {
							alreadyExists = true;
							break;
						}
					}
					
					if (!alreadyExists) {
						recipeService.loadRecipe(share);
						
						emailService.sendShareRecipeMessage(share);
					}
				}
			}
			
			status.setComplete();
			return view;
		}
	}
	
	@RequestMapping(params = "load", method = RequestMethod.POST)
	public String loadRemoteRecipe(@RequestParam("loadUrl") String url, Recipe recipe, BindingResult result, SessionStatus status) {
		final Recipe remoteRecipe;
		if (StringUtils.hasText(url)) {
			remoteRecipe = remoteRecipeService.parse(url);
		} else {
			remoteRecipe = new Recipe();
		}
		
		//TODO clear existing recipe data, don't merge it
		recipe.setName(remoteRecipe.getName());
		recipe.setInstructions(remoteRecipe.getInstructions());
		recipe.getIngredients().clear();
		recipe.getIngredients().addAll(remoteRecipe.getIngredients());
		recipe.setUrl(url);
		
		if (!StringUtils.hasText(remoteRecipe.getName())) { 
			if(!StringUtils.hasText(remoteRecipe.getInstructions()) && remoteRecipe.getIngredients().isEmpty()) {
				result.reject("recipe.load.failed", "Sorry we couldn't find a recipe on that site");
			} else {
				recipe.setName(url);
			}
		}
		
		return ADD_FORM_VIEW_NAME;
	}
	
	@RequestMapping(params = "edit", method = RequestMethod.POST)
	public String loadForEdit(@RequestParam("id") String recipeId, Recipe recipe, BindingResult result, SessionStatus status) {
		loadExistingRecipe(recipeId, recipe);
		
		return ADD_FORM_VIEW_NAME;
	}
	
	//TODO this is at path /list should be separated from other methods
	@RequestMapping(params = "delete", method = RequestMethod.POST)
	public String delete(@RequestParam("id") String recipeId, Recipe recipe, BindingResult result, SessionStatus status) {
		//TODO prevent deleting other user's recipes
		try {
			recipeService.deleteRecipe(recipeId);
			recipeService.deleteIngredientsByRecipeId(recipeId);
			recipeService.deleteSharesByRecipeId(recipeId);
			status.setComplete();
		} catch (Exception e) {
			result.reject("error.recipe.delete", e.getLocalizedMessage());
		}
		return "redirect:list";
		
	}
	
}
